/*
 * time_probe.cpp
 *
 *  Created on: 2008-9-8
 *      Author: fanliangliang
 */

#include "time_probe.h"
#include "data_table.h"
#include <sys/timeb.h>
#include <sstream>
#include <iostream>
#include <iomanip>
#include <algorithm>

BEGIN_NAMESPACE_TOXIC

using namespace std;

namespace detail {

	Timestamp::Timestamp(clock_t tick, time_t second, short millisecond)
	:ticks(tick)
	,seconds(second)
	,milliseconds(millisecond) {

	}

	Timestamp::Timestamp()
	:ticks(0)
	,seconds(0)
	,milliseconds(0) {

	}

	Timestamp::~Timestamp() {

	}

	Timestamp& Timestamp::operator-=(const Timestamp& rhs) {
		ticks -= rhs.ticks;
		if (milliseconds < rhs.milliseconds) {
			seconds -= 1;
			milliseconds += 1000;
		}
		seconds -= rhs.seconds;
		milliseconds -= rhs.milliseconds;

		if (seconds < 0) {
			seconds += 1;
			milliseconds = 1000 - milliseconds;
		}
		return *this;
	}

	Timestamp& Timestamp::operator+=(const Timestamp& rhs) {
		ticks += rhs.ticks;
		seconds += rhs.seconds;
		milliseconds += rhs.milliseconds;
		if (milliseconds > 1000) {
			seconds += 1;
			milliseconds -= 1000;
		}
		return *this;
	}

	Timestamp Timestamp::startTime() {
		timeb start;
		ftime(&start);
		clock_t tick = clock();
		return Timestamp(tick, start.time, start.millitm);
	}

	Timestamp Timestamp::endTime() {
		clock_t tick = clock();
		timeb end;
		ftime(&end);
		return Timestamp(tick, end.time, end.millitm);
	}



	TimeHolder::TimeHolder()
	:_map() {

	}

	TimeHolder::~TimeHolder() {
		clear();
	}

	void TimeHolder::increaseTime(const string& item, const Timestamp& ts) {
		ItemTimestampMap::iterator pos = _map.find(item);
		if (pos == _map.end()) {
			_map.insert(make_pair(item, new Timestamp(ts)));
		}
		else {
			*(pos->second) += ts;
		}
	}

	static string timeToString(time_t second, short millisecond) {
		ostringstream strm;
		strm << second << "." << setfill('0') << right << setw(3) << millisecond << setfill(' ');
		return strm.str();
	}

	void TimeHolder::report(ostream& ostrm) const {

		DataTable table;
		table.addColumn("Item Name");
		table.addColumn("Real Time(s)");
		table.addColumn("Cpu Time(s)");
		table.addColumn("Wasted Time(s)");

		ItemTimestampMap::const_iterator pos(_map.begin()), end(_map.end());
		for ( ; pos != end; ++pos) {
			DataRow* r = new DataRow();
			r->push_back(pos->first);

			Timestamp* ts = pos->second;
			r->push_back(timeToString(ts->seconds, ts->milliseconds));

			time_t second = ts->ticks / CLOCKS_PER_SEC;
			short millisecond =
				static_cast<short>((ts->ticks % CLOCKS_PER_SEC) / (CLOCKS_PER_SEC / 1000));

				r->push_back(timeToString(second, millisecond));

				Timestamp realTime(*ts);
				Timestamp cpuTime(0, second, millisecond);
				Timestamp wastedTime = realTime - cpuTime;
				r->push_back(timeToString(wastedTime.seconds, wastedTime.milliseconds));

				table.push_back(r);
		}

		ostrm << table;
	}

	void TimeHolder::clear() {
		ItemTimestampMap::iterator pos(_map.begin()), end(_map.end());
		for ( ; pos != end; ++pos) {
			delete pos->second;
		}
		_map.clear();
	}

} // namespace detail


detail::TimeHolder TimeProbe::_timeHolder;

TimeProbe::TimeProbe(const string& item, bool autoProbe /*= true*/)
:_item(item)
,_autoProbe(autoProbe)
,_startTime(detail::Timestamp::startTime()) {

}

TimeProbe::~TimeProbe() {
	if (_autoProbe) {
		stop();
	}
}

void TimeProbe::stop() {
	_timeHolder.increaseTime(_item, detail::Timestamp::endTime() - _startTime);
}

void TimeProbe::report(ostream& ostrm /*= cout*/) {
	_timeHolder.report(ostrm);
}

void TimeProbe::clear() {
	_timeHolder.clear();
}

END_NAMESPACE_TOXIC
